package zhuyun.code.launcheractivity.view.adapter

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import zhuyun.code.launcheractivity.model.data.interfaces.IExpandable
import zhuyun.code.launcheractivity.view.widget.component.LoadMoreView
import zhuyun.code.launcheractivity.view.widget.component.SimpleLoadMoreView
import java.lang.reflect.Constructor
import java.lang.reflect.Modifier
import java.lang.reflect.ParameterizedType

/**
 * @author: Administrator
 * @date: 2021/11/21
 */
abstract class BaseQuickAdapter<T,out:BaseViewHolder>(private val layoutResId:Int, private val data:MutableList<T>):RecyclerView.Adapter<out>() {

    private lateinit var mContext: Context
    private lateinit var mLayoutInflater: LayoutInflater
    companion object{
        const val LOADING_VIEW = 0x00000222
    }
    open fun onCreateDefViewHolder(parent: ViewGroup, viewType: Int):out{
        val view = mLayoutInflater.inflate(layoutResId,parent,false)
        return createBaseViewHolder(view)
    }

    override fun getItemViewType(position: Int): Int {
        return if(position < data.size){
            super.getItemViewType(position)
        } else {
            LOADING_VIEW
        }

    }
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): out {
        this.mContext = parent.context
        mLayoutInflater = LayoutInflater.from(mContext)
        return when(viewType){
            LOADING_VIEW ->{
                getLoadingView(parent)
            }
            else->{
                onCreateDefViewHolder(parent,viewType)
            }

        }

    }

    private val mLoadMoreView = SimpleLoadMoreView()

    private fun getLoadingView(parent:ViewGroup):out{
        val view = getItemView(mLoadMoreView.getLayoutId(),parent)
        return createBaseViewHolder(view)
    }

    private fun createBaseViewHolder(view: View): out {
        var temp:Class<*> = this.javaClass
        var z:Class<*>? = null
        while(z == null&&temp != null){
            z = getInstancedGenericKClass(temp)
            temp = temp.superclass
        }
        return if(z == null) BaseViewHolder(view) as out else {
            createGenericKInstance(z,view)
        }
    }

    protected fun createBaseViewHolder(parent:ViewGroup, layoutResId:Int):out{
        return createBaseViewHolder(getItemView(layoutResId,parent))
    }

    private fun getItemView(layoutResId:Int, parent:ViewGroup):View{
        return mLayoutInflater.inflate(layoutResId,parent,false)
    }


    private fun createGenericKInstance(z:Class<*>,view:View): out {
        var constructor:Constructor<*>
        return if(z.isMemberClass&&Modifier.isStatic(z.modifiers)){
            constructor = z.getDeclaredConstructor(this.javaClass,View::class.java)
            constructor.isAccessible = true
            constructor.newInstance(this,view) as out
        } else {
            constructor = z.getDeclaredConstructor(View::class.java)
            constructor.isAccessible = true
            constructor.newInstance(view) as out
        }
    }

    private fun getInstancedGenericKClass(z:Class<*>):Class<*>?{
        val type = z.genericSuperclass
        if(type is ParameterizedType){
            val types = (type as ParameterizedType).actualTypeArguments
            for(temp in types){
                if(temp is Class<*>){
                    val tempClass = temp as Class<*>
                    if(BaseViewHolder::class.java.isAssignableFrom(tempClass)){
                        return tempClass
                    }
                } else if(temp is ParameterizedType){
                    val rawType = (temp as ParameterizedType).rawType
                    if(rawType is Class<*>&&BaseViewHolder::class.java.isAssignableFrom(rawType as Class<*>)){
                        return rawType as Class<*>
                    }
                }
            }
        }
        return null
    }

    override fun getItemCount(): Int {
        return this.data.size + getLoadMoreViewCount()
    }

    override fun onBindViewHolder(holder: out, position: Int) {
        autoLoadMore(position)
        when(holder.itemViewType){
            LOADING_VIEW->{
                mLoadMoreView.convert(holder)
                holder.itemView.setOnClickListener{
                    if(mLoadMoreView.mLoadMoreStatus == LoadMoreView.STATUS_FAIL){
                        mLoadMoreView.mLoadMoreStatus = LoadMoreView.STATUS_DEFAULT
                        notifyItemChanged(getLoadMoreViewPosition())
                    }
                }
            }
            else->{
                convert(holder, this.data[position])
            }
        }

    }

    interface RequestLoadMoreListener{
        fun onLoadMoreRequested()
    }
    private var mRequestLoadMoreListener:RequestLoadMoreListener? = null
    fun setOnLoadMoreListener(requestLoadMoreListener: RequestLoadMoreListener){
        this.mRequestLoadMoreListener = requestLoadMoreListener
    }

    private fun getLoadMoreViewCount():Int{
        if(mRequestLoadMoreListener == null){
            return 0
        }
        return 1
    }
    private var mLoading = false
    private fun autoLoadMore(position: Int){
        if(position < itemCount - getLoadMoreViewCount()){
            return
        }
        if(mLoadMoreView.mLoadMoreStatus != LoadMoreView.STATUS_DEFAULT) {
            return
        }
        mLoadMoreView.mLoadMoreStatus = LoadMoreView.STATUS_LOADING
        if(!mLoading){
            mLoading = true
            mRequestLoadMoreListener?.onLoadMoreRequested()
        }

    }

    fun loadMoreEnd(){
        mLoading = false
        mLoadMoreView.mLoadMoreStatus = LoadMoreView.STATUS_END
        notifyItemChanged(getLoadMoreViewPosition())
    }

    fun loadMoreComplete(){
        mLoading = false
        mLoadMoreView.mLoadMoreStatus = LoadMoreView.STATUS_DEFAULT
        notifyItemChanged(getLoadMoreViewPosition())
    }

    fun loadMoreFail(){
        mLoading = false
        mLoadMoreView.mLoadMoreStatus = LoadMoreView.STATUS_FAIL
        notifyItemChanged(getLoadMoreViewPosition())
    }

    private fun getLoadMoreViewPosition():Int{
        return data.size
    }

    protected abstract fun convert(helper:out,item:T)

    fun collapse(position: Int):Int{
        return collapse(position, animate = true, notify = true)
    }

    private fun collapse(position:Int, animate: Boolean, notify: Boolean):Int{
        val expandable = getExpandableItem(position)?:return 0
        val subItemCount = recursiveCollapse(position)
        expandable.setExpanded(false)
        if(notify){
            if(animate){
                notifyItemChanged(position)
                notifyItemRangeRemoved(position+1,subItemCount)
            } else {
                notifyDataSetChanged()
            }
        }
        return subItemCount




    }

    private fun getItemPosition(item:T):Int{
        return if(data.isEmpty()) -1 else data.indexOf(item)
    }


    private fun recursiveCollapse(position: Int):Int{
        val item = getItem(position)
        if(!isExpandable(item)){
            return 0
        }
        val expandable = item as IExpandable<T>
        var subItemCount = 0
        if(expandable.isExpanded()){
            val subItems = expandable.getSubItems()
            for(i in subItems.size-1 downTo 0){
                val subItem = subItems[i]
                val pos = getItemPosition(subItem)
                if(pos < 0){
                    continue
                }
                if(isExpandable(subItem)){
                    subItemCount += recursiveCollapse(pos)
                }
                data.removeAt(pos)
                subItemCount++
            }
        }
        return subItemCount

    }



    fun expandAll(){
        for(i in this.data.size -1 downTo 0){
            expandAll(i, animate = false, notify = false)
        }
    }

    private fun expandAll(position:Int, animate:Boolean, notify:Boolean):Int{
        val expandable = getExpandableItem(position) ?: return 0
        if(!hasSubItems(expandable)){
            expandable.setExpanded(true)
            notifyItemChanged(position)
            return 0
        }
        val count = expand(position, animate = false, shouldNotify = false)
        if(notify){
            if(animate){
                notifyItemRangeInserted(position+1,count)
            } else {
                notifyDataSetChanged()
            }
        }
        return count

    }

    private fun recursiveExpand(position: Int,list:List<T>):Int{
        var count = list.size
        var pos = position +list.size -1
        for(i in list.size -1 downTo 0){
            if(list[i] is IExpandable<*>){
                val item = list[i] as IExpandable<T>
                if(!item.isExpanded()&&hasSubItems(item)){
                    val subList = item.getSubItems()
                    this.data.addAll(pos+1,subList)
                    val subItemCount = recursiveExpand(pos+1,subList)
                    count += subItemCount
                }

            }
        }
        return count
    }

    fun expand(position: Int):Int{
        return expand(position, animate = true, shouldNotify = true)
    }

    fun expand(position: Int,animate: Boolean,shouldNotify:Boolean):Int{
        val expandable = getExpandableItem(position)?:return 0
        if(!hasSubItems(expandable)){
            expandable.setExpanded(true)
            notifyItemChanged(position)
            return 0
        }
        var subItemCount = 0
        if(!expandable.isExpanded()){
            val list = expandable.getSubItems()
            this.data.addAll(position+1,list)
            subItemCount += recursiveExpand(position+1,list)
            expandable.setExpanded(true)
        }
        if(shouldNotify){
            if(animate){
                notifyItemChanged(position)
                notifyItemRangeInserted(position+1,subItemCount)
            } else {
                notifyDataSetChanged()
            }
        }
        return subItemCount

    }

    private fun hasSubItems(item:IExpandable<T>):Boolean{
         item?:return false
        val list = item.getSubItems()
        return list.isNotEmpty()
    }

    private fun getExpandableItem(position:Int):IExpandable<T>?{
        val item = getItem(position)
        return if(isExpandable(item)){
            item as IExpandable<T>
        } else {
            null
        }


    }

    fun isExpandable(item:T?):Boolean{
        return item != null&&item is IExpandable<*>
    }

    fun getItem(position:Int):T?{
        if(position >= 0&&position < this.data.size){
            return this.data[position]
        }
        return null
    }

}